package com.duang.framework.dao;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.esotericsoftware.reflectasm.MethodAccess;

public class Model {

	/**
	 * full name of class
	 */
	private Class<?> clazz;
	private Property primaryKey;
	private String tableName;
	private Map<String, Property> propertyMap = new HashMap<String, Property>();
	private List<Property> propertyList = new ArrayList<Property>();

	public static Model getModel(Class<?> clazz) {
		Model model = new Model();
		model.setClazz(clazz);
		if (clazz.isAnnotationPresent(Table.class)) {
			Table table = clazz.getAnnotation(Table.class);
			if (table.value() != null && !table.value().equals("")) {
				model.setTableName(table.value());
			} else {
				model.setTableName(clazz.getSimpleName());
			}
		}

		MethodAccess methodAccess = MethodAccess.get(clazz);
		for (Field field : clazz.getDeclaredFields()) {
			if (field.isAnnotationPresent(Column.class)) {
				Property property = new Property();
				property.setMethodAccess(methodAccess);
				Column column = field.getAnnotation(Column.class);
				String columnName = column.value() != null
						&& !column.value().equals("") ? column.value() : field
						.getName();
				property.setColumnName(columnName);
				PropertyDescriptor propertyDescriptor = null;
				try {
					propertyDescriptor = new PropertyDescriptor(
							field.getName(), clazz);
				} catch (IntrospectionException e) {
					e.printStackTrace();
				}
				property.setSetMethodIndex(methodAccess
						.getIndex(propertyDescriptor.getWriteMethod().getName()));
				property.setGetMethodIndex(methodAccess
						.getIndex(propertyDescriptor.getReadMethod().getName()));
				if (column.primaryKey()) {
					model.setPrimaryKey(property);
				} else {
					model.getPropertyList().add(property);
				}
				model.propertyMap.put(field.getName(), property);
			}
		}
		return model;

	}

	public String getColumnNameByFieldName(String name) {
		return propertyMap.get(name).getColumnName();
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public List<Property> getPropertyList() {
		return propertyList;
	}

	public void setPropertyList(List<Property> propertyList) {
		this.propertyList = propertyList;
	}

	public Class<?> getClazz() {
		return clazz;
	}

	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}

	public Property getPrimaryKey() {
		return primaryKey;
	}

	public void setPrimaryKey(Property primaryKey) {
		this.primaryKey = primaryKey;
	}

	public static class Property {
		private MethodAccess methodAccess;
		private int getMethodIndex;
		private int setMethodIndex;
		private String columnName;

		public MethodAccess getMethodAccess() {
			return methodAccess;
		}

		public void setMethodAccess(MethodAccess methodAccess) {
			this.methodAccess = methodAccess;
		}

		public void invokeWriterMethod(Object obj, Object... args) {
			// getWriterMethod().invoke(obj, args);
			methodAccess.invoke(obj, setMethodIndex, args);
		}

		public Object invokeReadMethod(Object obj) {
			return methodAccess.invoke(obj, getMethodIndex);
		}

		public Method getReadMethod() {
			// return methodAccess.invoke(object, methodIndex, args);
			return null;
		}

		public String getColumnName() {
			return columnName;
		}

		public void setColumnName(String columnName) {
			this.columnName = columnName;
		}

		public int getGetMethodIndex() {
			return getMethodIndex;
		}

		public void setGetMethodIndex(int getMethodIndex) {
			this.getMethodIndex = getMethodIndex;
		}

		public int getSetMethodIndex() {
			return setMethodIndex;
		}

		public void setSetMethodIndex(int setMethodIndex) {
			this.setMethodIndex = setMethodIndex;
		}

	}

}
